﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Looptribe.Moodlin8.Model;
using Looptribe.Moodlin8.Model.Api;

namespace Looptribe.Moodlin8.ViewModel
{
    public class SearchResultsViewModel : LoadingViewModel
    {
        private string _queryText;
        public string QueryText
        {
            get { return _queryText; }
            private set
            {
                if (_queryText != value)
                {
                    _queryText = value;
                    RaisePropertyChanged(() => QueryText);
                }
            }
        }

        private readonly ObservableCollection<GroupInfoList> _results = new ObservableCollection<GroupInfoList>();
        public ObservableCollection<GroupInfoList> Results
        {
            get { return _results; }
        }

        private readonly Filter[] _filters = new Filter[0];
        public IEnumerable<Filter> Filters
        {
            get { return _filters; }
        }

        private IEnumerable<Assignment> _assignments;
        private IEnumerable<Course> _courses;
        private IEnumerable<CourseUser> _contacts;

        public bool ShowFilters
        {
            get { return _filters.Length > 1; }
        }

        public async Task LoadContent(string queryText)
        {
            QueryText = queryText;

            IsLoading = true;

            if (!await Controller.LoginAsync())
                throw new InvalidOperationException("Login failed"); // TODO: redirect to login

            _assignments = (await Api.GetAssignmentsAsync(null)).Data;
            _courses = (await Api.GetEnrolledCoursesAsync(CurrentUser)).Data;
            _contacts = (await Api.GetContactsAsync()).Data;

            IsLoading = false;

            UpdateResults();
        }

        public void UpdateResults()
        {
            Results.Clear();
            var queryText = QueryText.ToLowerInvariant();

            if (_assignments != null)
            {
                var list = new GroupInfoList("Assignments");
                var filtered = _assignments
                    .Where(a => a.Name.ToLowerInvariant().Contains(queryText))
                    .OrderBy(a => a.DueDate.HasValue ? a.DueDate.Value : DateTime.MaxValue);
                foreach (var wrapper in filtered.Select(a => new AssignmentWrapper(a, _courses.FirstOrDefault(c => c.Id == a.Course))))
                    list.Add(wrapper);
                if (list.Count > 0)
                    Results.Add(list);
            }

            if (_contacts != null)
            {
                var list = new GroupInfoList("Contacts");
                var filtered = _contacts
                    .Where(a => a.Fullname.ToLowerInvariant().Contains(queryText))
                    .OrderBy(a => a.Fullname);
                foreach (var wrapper in filtered.Select(a => new CourseUserWrapper(a)))
                    list.Add(wrapper);
                if (list.Count > 0)
                    Results.Add(list);
            }

            if (_courses != null)
            {
                var list = new GroupInfoList("Courses");
                var filtered = _courses
                    .Where(a => a.Shortname.ToLowerInvariant().Contains(queryText))
                    .OrderBy(a => a.Shortname);
                foreach (var wrapper in filtered.Select(a => new CoursesCourseWrapper(a)))
                    list.Add(wrapper);
                if (list.Count > 0)
                    Results.Add(list);
            }
        }

        #region Inner classes

        public class GroupInfoList : ObservableCollection<object>
        {
            private readonly object _key;
            public object Key { get { return _key; } }

            public GroupInfoList(object key)
            {
                _key = key;
            }
        }

        /// <summary>
        /// View model describing one of the filters available for viewing search results.
        /// </summary>
        public sealed class Filter : Common.BindableBase
        {
            private String _name;
            private int _count;
            private bool _active;

            public Filter(String name, int count, bool active = false)
            {
                Name = name;
                Count = count;
                Active = active;
            }

            public override String ToString()
            {
                return Description;
            }

            public String Name
            {
                get { return _name; }
                set { if (SetProperty(ref _name, value)) OnPropertyChanged("Description"); }
            }

            public int Count
            {
                get { return _count; }
                set { if (SetProperty(ref _count, value)) OnPropertyChanged("Description"); }
            }

            public bool Active
            {
                get { return _active; }
                set { SetProperty(ref _active, value); }
            }

            public String Description
            {
                get { return String.Format("{0} ({1})", _name, _count); }
            }
        }

        #endregion
    }
}
